package com.ruoyi.workflow.service.impl;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.workflow.domain.BpmnInfo;
import com.ruoyi.workflow.domain.BpmnNode;
import com.ruoyi.workflow.mapper.BpmnInfoMapper;
import com.ruoyi.workflow.mapper.BpmnNodeMapper;
import com.ruoyi.workflow.service.IBpmnInfoService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;

/**
 * 流程定义Service业务层处理
 *
 * @author xiaodu
 * @date 2023-06-17
 */
@Service
public class BpmnInfoServiceImpl extends ServiceImpl<BaseMapper<BpmnInfo>, BpmnInfo> implements IBpmnInfoService {
    /**
     * 支持的后缀
     */
    private static final List<String> SUPPORT_SUFFIXES = Arrays.asList("bpmn", "xml", "zip", "svg");

    @Autowired
    private BpmnInfoMapper bpmnInfoMapper;
    @Autowired
    private BpmnNodeMapper bpmnNodeMapper;

    /**
     * 查询流程定义列表
     *
     * @param bpmnInfo 流程定义
     * @return 流程定义
     */
    @Override
    public List<BpmnInfo> selectBpmnInfoList(BpmnInfo bpmnInfo) {
        return bpmnInfoMapper.selectBpmnInfoList(bpmnInfo);
    }

    /**
     * 部署流程定义
     *
     * @param bpmnInfo 流程部署数据
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deploy(MultipartFile[] fileList, BpmnInfo bpmnInfo) throws IOException {
        if(fileList == null && fileList.length == 0){
            throw new ServiceException("文件对象为 null");
        }
        //1.流程文件部署
        //1.1 获取流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //1.2 获取资源service
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //部署对象
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        for (MultipartFile file : fileList) {
            //获取文件名
            String filename = file.getOriginalFilename();
            //0.判断文件是否有该后缀
            String fileSuffix = FileUtils.getNameSuffix(filename);
            if (!SUPPORT_SUFFIXES.contains(fileSuffix)) {
                throw new ServiceException("目前仅支持提交" + SUPPORT_SUFFIXES + "类型的流程文件");
            }
            InputStream in = file.getInputStream();

            //1.3 部署流程文件
            //1.3.1 判断是上传哪种文件类型
            // .zip
            if ("zip".equals(fileSuffix)) {
                deploymentBuilder.addZipInputStream(new ZipInputStream(in));
            } else {
                //.xml 或 .bpmn 或 .svg
                deploymentBuilder.addInputStream(filename, in);
            }
        }
        //遍历完成后统一部署,这样能确保同一资源流程部署id相同
        Deployment deployment = deploymentBuilder.deploy();

        //流程文件信息到bus_bpmn_info表中
        //1.4 获取流程定义对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        if (processDefinition == null) {
            throw new ServiceException("流程定义key不正确！");
        }
        //2.把数据封装到bpmnInfo中
        bpmnInfo.setProcessDefineKey(processDefinition.getKey());
        bpmnInfo.setDeploymentId(processDefinition.getDeploymentId());
        bpmnInfo.setDeployTime(deployment.getDeploymentTime());
        bpmnInfo.setVersion(processDefinition.getVersion());
        //3.部署流程
        bpmnInfoMapper.insertBpmnInfo(bpmnInfo);

    }


    /**
     * 删除流程定义信息
     *
     * @param deploymentId 流程定义主键
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deleteBpmnInfoById(Long bpmnInfoId,String deploymentId) {
        //删除流程定义前：先判断是否有节点，有则不允许删除
        BpmnNode bpmnNode = new BpmnNode();
        bpmnNode.setBpmnInfoId(bpmnInfoId);
        List<BpmnNode> bpmnNodeList = bpmnNodeMapper.selectBpmnNodeList(bpmnNode);
        if (!CollectionUtils.isEmpty(bpmnNodeList)) {
            throw new ServiceException("删除失败,该流程有关联的节点");
        }

        //1.删除无启动流程实例的流程文件
        //1.1 获取流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //1.2 获取资源service
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //1.3 根据流程部署id删除流程定义信息
        repositoryService.deleteDeployment(deploymentId);

        //2.根据流程部署id删除bus_bpmn_info表的信息
        Map<String, Object> map = new HashMap<String, Object>() {{
            this.put("deployment_id", deploymentId);
        }};
        return super.removeByMap(map);
    }

    /**
     * 查看流程文件/图
     *
     * @param deploymentId 流程部署的ID
     * @param fileType     流程文件的类型 file/img
     * @return 输入流
     */
    @Override
    public InputStream readResourceFile(String deploymentId, String fileType) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        if (processDefinition == null) {
            throw new ServiceException("流程定义key不正确！");
        }
        //从流程定义获取deploymentId , key
        //判断是获取流程文件还是流程图
        InputStream in = null;
        if ("img".equalsIgnoreCase(fileType)) {
            // 通过淡程定义对象的图片资源名称，判断是否有图片资源
            String svgName = processDefinition.getDiagramResourceName();
            // 如果有图片，直接通过仓库 service接口获取图片流对象并返回   前提：支持多文件上传
            if(svgName != null && !"".equals(svgName)){
                in = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), svgName);
                return in;
            }

            //如果没图片，则根据流程文件生成流程图
            //获取流程图片
            //1.获取流程图生成对象
            DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
            /**
             * bpmnModel- bpmn模型获取
             * highlightedactivity -用于指定需要高亮显示的活动节点ID（例如正在执行的任务）
             * highLightedFlows -用于指定需要高亮显示的连线ID（例如正在进行的流转路径）
             * activityFontName -覆盖默认活动字体
             * labelFontName -覆盖默认标签字体
             */
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
            in = generator.generateDiagram(bpmnModel,
                    Collections.EMPTY_LIST,
                    Collections.EMPTY_LIST,
                    "宋体",
                    "宋体",
                    "宋体");
        } else if ("file".equalsIgnoreCase(fileType)) {
            //获取流程文件
            in = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        }
        return in;
    }
}
